
/*
 * Serial.s
 *
 * Created: 15.7.2013 9:24:36
 *  Author: Ferda
 */ 

#include <avr/io.h>
#include <avr/interrupt.h>

//==========================================================

.section .bss

; receive ring buffer
rx_read_index:	.byte	0		; index of next read operation
rx_write_index:	.byte	0		; index of next write operation
rx_buffer:		.skip	256		; 256bytes of buffer
				

; transmit ring buffer
tx_read_index:	.byte	0		; index of next read operation
tx_write_index:	.byte	0		; index of next write operation
tx_buffer:		.skip	256		; 256bytes of buffer
	

//==========================================================

.section .text

.global USART1_flush_rx
.global USART1_flush_tx
/**********************************************************
* Received character interrupt
* 
* Store recived character to buffer at write position and 
* increment write index by 1
**********************************************************/

.global USART1_RX_vect

USART1_RX_vect:
		push	r16							; store used register
		in		r16, _SFR_IO_ADDR(SREG)
		push	r16
		push	r17
		push	r30
		push	r31
		;-------------------------------
		lds		r16, rx_read_index			; r16 = read index
		lds		r17, rx_write_index			; r17 = write index
		inc		r17							; r17 = next write index		
		cp		r16, r17					; test if read index == next write index	
		brne	USART1_RX_vect_store		; buffer not full, store it
		lds		r16, UDR1					; must read to disable interrupt loop
		rjmp	USART1_RX_vect_end

USART1_RX_vect_store:
		; buffer neni plny, ulozim dalsi byte
		sts		rx_write_index, r17			; ulozim novou hodnotu write indexu, jsem v preruseni a nikdo toho nevyuzije
		dec		r17							; zpet na polohu pro zapis

		ldi		ZL, lo8(rx_buffer)			; adresa bufferu
		ldi		ZH, hi8(rx_buffer)
		clr		r16
		add		ZL, r17						; vypocet offsetu pro ulozeni
		adc		ZH, r16
			
		lds		r16, UDR1					; nacteni prijateho bytu
		st		Z, r16						; ulozeni prijateho bytu na spravnou adresu bufferu
		;-------------------------------
USART1_RX_vect_end:
		pop		r31							; restore used registers
		pop		r30
		pop		r17
		pop		r16
		out		_SFR_IO_ADDR(SREG), r16
		pop		r16
		reti

/*********************************************
* USART1 tx register empty interrupt
*
* pushes R31:R30 and R17:R16
**********************************************/

.global USART1_UDRE_vect

USART1_UDRE_vect:
		push	r16							; store used reg
		in		r16, _SFR_IO_ADDR(SREG)
		push	r16
		push	r17
		push	r30
		push	r31
		;--------------------------------
		lds		r16, tx_read_index			; r16 = read index
		lds		r17, tx_write_index			; r17 = write index
		cp		r16, r17
		brne	USART1_UDRE_vect_send		; indexs not same => buffer is not empty => can send byte
		
		lds		r17, UCSR1B					; buffer is empty
		andi	r17, ~(1 << UDRIE1)			; disable next
		sts		UCSR1B, r17					; interrupt
		rjmp	USART1_UDRE_vect_end

USART1_UDRE_vect_send:
		ldi		ZL, lo8(tx_buffer)
		ldi		ZH, hi8(tx_buffer)

		clr		r17
		add		ZL, r16					; move z to read location
		adc		ZH, r17
		ld		r17, Z					; load character to send 
		sts		UDR1, r17

		inc		r16						; store next read index
		sts		tx_read_index, r16			

		;--------------------------------
USART1_UDRE_vect_end:
		pop		r31
		pop		r30
		pop		r17
		pop		r16							; restore used reg
		out		_SFR_IO_ADDR(SREG), r16
		pop		r16
		reti

/**********************************************************
* USART1 inicialization
*
* R25:R24 baudrate
*
* Modify R31:R30 and R25:R24
***********************************************************/

.global USART1_begin

USART1_begin:
		sbi		_SFR_IO_ADDR(DDRD), DDD3	; TXD as output
		cbi		_SFR_IO_ADDR(DDRD), DDD2	; RXD as input
		sts		rx_read_index, r1
		sts		rx_write_index, r1
		sts		tx_read_index, r1
		sts		tx_write_index, r1

		ldi		ZL, UCSR1A					; base address of usart 1
		clr		ZH

		std		Z+4, r24
		std		Z+5, r25					; set baud rate

		ldi		r24, (1 << U2X1)			; set double speed mode
		std		Z+0, r24

		ldi		r24, (1 << UCSZ11) | (1 << UCSZ10)
		std		Z+2, r24

		ldi		r24, (1 << RXCIE1) | (1 << RXEN1) | (1 << TXEN1)
		std		Z+1, r24

		ret

/**********************************************************
* USART1 uninicialization
*
*
* Modify R24
***********************************************************/
.global USART1_end

USART1_end:
		call	USART1_flush_tx

		ldi		ZL, UCSR1A
		clr		ZH

		std		Z+1, r1
		std		Z+0, r1
		ldi		r24, (1 << UCSZ11) | (1 << UCSZ10)
		std		Z+2, r24
		std		Z+4, r1
		std		Z+5, r1

		cbi		_SFR_IO_ADDR(DDRD), DDD3	; TXD to input
		cbi		_SFR_IO_ADDR(DDRD), DDD2	; RXD to input

		ret
		
/*********************************************************
* Send byte
*  R24 - byte to send
*
*  Modify R25, R31:R30
***********************************************************/

.global USART1_write_byte

USART1_write_byte:						
		lds		r25, UCSR1B					; test if loading from buffer is started
		sbrc	r25, UDRIE1					; register is ready for send
		rjmp	USART1_write_byte_init		; loading from buffer running
		
		lds		r25, UCSR1A					; test if transmit register is ready to write
		sbrs	r25, UDRE1					;
		rjmp	USART1_write_byte_init		; register is not ready to write byte, put it to buffer

		sts		UDR1, r24
		ret

USART1_write_byte_init:
		lds		r25, tx_write_index
		inc		r25							; r19 next write index

USART1_send_byte_wait:		
		lds		r30, tx_read_index			; r18 read index
		cp		r25, r30
		breq	USART1_send_byte_wait
		dec		r25							; back to actual write position

		ldi		ZL, lo8(tx_buffer)			; load buffer address
		ldi		ZH, hi8(tx_buffer)
		add		ZL, r25						; move to write location
		adc		ZH, r1
		st		Z, r24						; store character to write location

		inc		r25
		sts		tx_write_index, r25			; store new index

		lds		r24, UCSR1B					; enable UDRIE interrupt
		ori		r24, (1 << UDRIE1)
		sts		UCSR1B, r24
		
		ret

/*****************************************************
* get status received bufferu
*
* return R24: 255 == unreaded char/s in buffer
*			  0 == buffer is empty
*
* Modify R25
*******************************************************/

.global	USART1_available

USART1_available:
		lds		r24, rx_read_index
		lds		r25, rx_write_index
		cp		r24, r25
		brne	USART1_available_yes
		clr		r24
		ret
USART1_available_yes:
		ser		r24
		ret

/******************************************************
* Read byte from receive buffer, if buffer is empty wait
*
* return R24 - unreaded char
*
* Modify R31:R30 and R25
*******************************************************/

.global USART1_read_byte

USART1_read_byte:
		lds		r25, rx_read_index

USART1_read_byte_wait:
		lds		r24, rx_write_index
		cp		r24, r25
		breq	USART1_read_byte_wait	;	indexs are same => buffer is empty => wait
		
		ldi		ZL, lo8(rx_buffer);
		ldi		ZH, hi8(rx_buffer);
		add		ZL, r25
		adc		ZH, r1
		ld		r24, Z					; read byte from buffer

		inc		r25
		sts		rx_read_index, r25		; store new read index

		ret								; return




/********************************************************************
* Invalidate all data in rx buffer
*
* Modify R24
**********************************************************************/
USART1_flush_rx:
		lds		r24, rx_write_index
		sts		rx_read_index, r24
		ret


/********************************************************************
* Flush transmit buffer
*
* Modify R25:R24
**********************************************************************/


USART1_flush_tx:
		lds		r24, tx_write_index		; wait to empty buffer
USART1_flush_tx1:
		lds		r25, tx_read_index
		cp		r24, r25
		brne	USART1_flush_tx1

USART1_flush_tx2:						; wait to send register is empty
		lds		r24, UCSR1A
		sbrs	r24, UDRE1
		rjmp	USART1_flush_tx2

		ldi		r25, 40					; wait to current transmit timeout
		clr		r24
USART1_flush_tx3:
		dec		r24
		brne	USART1_flush_tx3
		dec		r25
		brne	USART1_flush_tx3

		ret

.end